package com.cardone.persistent.support;

import java.util.List;
import java.util.Map;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.Setter;
import lombok.experimental.Accessors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
import org.springframework.util.CollectionUtils;

import com.cardone.common.dto.PaginationDto;
import com.cardone.common.template.support.TemplateSupport;
import com.cardone.common.utils.PaginationUtils;
import com.cardone.persistent.builder.ModelBuilder;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * jdbc dao
 * 
 * @author yaoht
 */
@Getter
@Setter
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class NamedParameterJdbcTemplateSupport implements JdbcTemplateSupport {
	/**
	 * namedParameterJdbcTemplateRouterSupport
	 */
	private NamedParameterJdbcTemplateRouterSupport namedParameterJdbcTemplateRouterSupport;

	/**
	 * 模板支持
	 */
	private TemplateSupport templateSupport;

	@Override
	public String builderSql(@NonNull final String id, final Map<String, Object> model) {
		final String sql = this.templateSupport.process(id, model);

		return sql;
	}

	@Override
	public <R> R find(@NonNull final Class<R> mappedClass, @NonNull final String id, final Map<String, Object> model) {
		final String sql = this.builderSql(id, model);

		try {
			return this.getNamedParameterJdbcTemplate(id, model).queryForObject(sql, model,
			        ParameterizedBeanPropertyRowMapper.newInstance(mappedClass));
		} catch (final EmptyResultDataAccessException e) {
			return null;
		}
	}

	@Override
	public Map<String, Object> find(@NonNull final String id, final Map<String, Object> model) {
		final String sql = this.builderSql(id, model);

		return this.getNamedParameterJdbcTemplate(id, model).queryForMap(sql, model);
	}

	@Override
	public <R> List<R> findList(@NonNull final Class<R> mappedClass, @NonNull final String id, final Map<String, Object> model) {
		final String sql = this.builderSql(id, model);

		return this.getNamedParameterJdbcTemplate(id, model).query(sql, model,
		        ParameterizedBeanPropertyRowMapper.newInstance(mappedClass));
	}

	@Override
	public List<Map<String, Object>> findList(@NonNull final String id, final Map<String, Object> model) {
		final String sql = this.builderSql(id, model);

		return this.getNamedParameterJdbcTemplate(id, model).queryForList(sql, model);
	}

	@Override
	public org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate getNamedParameterJdbcTemplate(@NonNull String value,
	        Object model) {
		return this.namedParameterJdbcTemplateRouterSupport.getNamedParameterJdbcTemplate(value, model);
	}

	@Override
	public <R> PaginationDto<R> pagination(@NonNull final Class<R> mappedClass, @NonNull PaginationArgs paginationArgs,
	        Map<String, Object> model) {
		Map<String, Object> newModel;

		if (model == null) {
			newModel = Maps.newHashMap();
		} else {
			newModel = Maps.newHashMap(model);
		}

		final long totalSize = this.read(Long.class, paginationArgs.getReadId(), null, newModel);

		final PaginationDto<R> pagination = PaginationUtils.newPagination(paginationArgs.getNo(), paginationArgs.getSize(), 6,
		        totalSize);

		if (totalSize < 1) {
			List<R> dataList = Lists.newArrayList();

			pagination.setDataList(dataList);

			return pagination;
		}

		if (totalSize < pagination.getBeginRowNum()) {
			List<R> dataList = Lists.newArrayList();

			pagination.setDataList(dataList);

			return pagination;
		}

		newModel.put(ModelBuilder.Constants.beginRowNum.stringValue(), pagination.getBeginRowNum());

		newModel.put(ModelBuilder.Constants.endRowNum.stringValue(), pagination.getEndRowNum());

		List<R> dataList = this.findList(mappedClass, paginationArgs.getFindId(), newModel);

		pagination.setDataList(dataList);

		return pagination;
	}

	@Override
	public PaginationDto<Map<String, Object>> pagination(@NonNull PaginationArgs paginationArgs, Map<String, Object> model) {
		Map<String, Object> newModel;

		if (model == null) {
			newModel = Maps.newHashMap();
		} else {
			newModel = Maps.newHashMap(model);
		}

		final long totalSize = this.read(Long.class, paginationArgs.getReadId(), null, newModel);

		final PaginationDto<Map<String, Object>> pagination = PaginationUtils.newPagination(paginationArgs.getNo(),
		        paginationArgs.getSize(), 6, totalSize);

		if (totalSize < 1) {
			List<Map<String, Object>> dataList = Lists.newArrayList();

			pagination.setDataList(dataList);

			return pagination;
		}

		if (totalSize < pagination.getBeginRowNum()) {
			List<Map<String, Object>> dataList = Lists.newArrayList();

			pagination.setDataList(dataList);

			return pagination;
		}

		newModel.put(ModelBuilder.Constants.beginRowNum.stringValue(), pagination.getBeginRowNum());

		newModel.put(ModelBuilder.Constants.endRowNum.stringValue(), pagination.getEndRowNum());

		List<Map<String, Object>> dataList = this.findList(paginationArgs.getFindId(), newModel);

		pagination.setDataList(dataList);

		return pagination;
	}

	@Override
	public <R> R read(@NonNull final Class<R> requiredType, @NonNull final String id, final String objectId, Map<String, Object> model) {
		Map<String, Object> newModel;

		if (model == null) {
			newModel = Maps.newHashMap();
		} else {
			newModel = Maps.newHashMap(model);
		}

		if (StringUtils.isNotBlank(objectId)) {
			newModel.put(ModelBuilder.Constants.objectId.stringValue(), objectId);
		}

		final String sql = this.builderSql(id, newModel);

		return this.getNamedParameterJdbcTemplate(id, newModel).queryForObject(sql, newModel, requiredType);
	}

	@Override
	public <R> List<R> readList(@NonNull final Class<R> elementType, @NonNull final String id, final String objectId,
	        Map<String, Object> model) {
		Map<String, Object> newModel;

		if (model == null) {
			newModel = Maps.newHashMap();
		} else {
			newModel = Maps.newHashMap(model);
		}

		if (StringUtils.isNotBlank(objectId)) {
			newModel.put(ModelBuilder.Constants.objectId.stringValue(), objectId);
		}

		final String sql = this.builderSql(id, newModel);

		return this.getNamedParameterJdbcTemplate(id, newModel).queryForList(sql, newModel, elementType);
	}

	@Override
	public int update(@NonNull final String id, final List<Map<String, Object>> modelList) {
		if (CollectionUtils.isEmpty(modelList)) {
			return 0;
		}

		int rowCount = 0;

		for (final Map<String, Object> model : modelList) {
			rowCount += this.update(id, model);
		}

		return rowCount;
	}

	@Override
	public int update(@NonNull final String id, final Map<String, Object> model) {
		final String sql = this.builderSql(id, model);

		return this.getNamedParameterJdbcTemplate(id, model).update(sql, model);
	}
}
